Sys.setenv(LANG = "en")
library(pROC)
Type 'citation("pROC")' for a citation.

Attaching package: ‘pROC’

The following objects are masked from ‘package:stats’:

    cov, smooth, var
setwd("C:/Users/halan/Desktop/FPU/SPring2025/StatisticalLearning")

incidents <- read.csv("mass_killing_incidents_public.csv")
offenders <- read.csv("mass_killing_offenders_public.csv")
victims <- read.csv("mass_killing_victims_public.csv")
weapons <- read.csv("mass_killing_weapons_public.csv")
view(incidents)
view(offenders)
view(victims)
view(weapons)

DATA: Mass Killing Database 2006 - Present (AS OF MARCH 15th 2025)

Mass Murder for this dataset is defined as “…the intentional killing of four or more victims by any means within a 24-hour period, excluding the death of unborn children and the offender(s).)

This Database is constantly being updated (Was just updated 2 hours ago as of March 16th, 2025 at 12:50PM) and I would say is very trustworthy. The contact details for this Database is Justin Myers. Justin Myers is a veteran journalist for “The Associated Press”, they are also working with Northeastern University’s James Alan Fox whom is also a contact.

Features(Incident [19 Columns x 615 Rows]):

Red = Categorical | Green = Numerical

Features(Victims [6 Columns x 3174 Rows]):

Features(Weapons [5 Columns x 963 Rows]):

Features(Offenders [15 Columns x 788 Rows]):

glimpse(incidents)
Rows: 615
Columns: 19
$ incident_id           <int> 644, 643, 642, 639, 641, 638, 637, 636, 635, 634, 632, 631, 629, 630, 627, 626, 625, 624, 623, 620, 619, 618, 616, 615, 614, 613…
$ date                  <chr> "2025-02-21", "2025-02-10", "2025-01-01", "2024-12-14", "2024-12-14", "2024-12-07", "2024-11-16", "2024-11-10", "2024-11-07", "2…
$ city                  <chr> "Lake Station", "Byron", "New Orleans", "Belen", "West Valley", "DeKalb County", "Lancaster", "Wichita", "Duluth", "Fall City", …
$ state                 <chr> "IN", "WY", "LA", "NM", "UT", "GA", "CA", "KS", "MN", "WA", "AL", "GA", "IL", "NY", "NY", "GA", "FL", "NY", "AL", "AL", "CA", "K…
$ num_offenders         <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 5, 1, 1, 1, 3, 1, 1,…
$ num_victims_killed    <int> 4, 4, 14, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 5, 6, 4, 8, 4, 4, 6, 4…
$ num_victims_injured   <int> 0, 0, 30, 0, 1, 0, 0, 0, 0, 1, 17, 9, 0, 0, 0, 0, 0, 0, 0, 10, 0, 3, 1, 9, 0, 0, 4, 0, 7, 0, 0, 7, 0, 0, 1, 2, 0, 0, 1, 0, 0, 3,…
$ firstcod              <chr> "Shooting", "Shooting", "Vehicle crash", "Shooting", "Shooting", "Shooting", "Shooting", "Shooting", "Shooting", "Shooting", "Sh…
$ secondcod             <chr> "", "", "", "", "", "", "", "", "", "", "", "", "", "Smoke inhalation & burns", "", "", "", "Stabbing", "", "", "", "", "", "", …
$ if_assault_rifle_used <chr> "Unknown gun type", "Gun not an assault rifle", "Nongun", "Gun not an assault rifle", "Unknown gun type", "Unknown gun type", "G…
$ type                  <chr> "Family", "Family", "Public", "Family", "Family", "Family", "Undetermined", "Family", "Family", "Family", "Suspected felony", "P…
$ situation_type        <chr> "Family issue", "Despondency", "Terrorism", "", "", "Family issue", "", "", "", "Family issue", "Other", "Indiscriminate", "Unde…
$ location_type         <chr> "Residence/Other shelter", "Residence/Other shelter", "Open space", "Residence/Other shelter", "Residence/Other shelter", "Resid…
$ location              <chr> "Residence", "Residence", "Open space", "Residence", "Residence", "Residence", "Residence", "Residence", "Residence", "Residence…
$ longitude             <dbl> -87.25547, -108.50082, -90.06991, -106.68182, -111.97256, -84.31904, -118.11650, -97.32347, -92.15961, -121.88448, -86.79633, -8…
$ latitude              <dbl> 41.59381, 44.79471, 29.95388, 34.56935, 40.69044, 33.72203, 34.69170, 37.65839, 46.74984, 47.53114, 33.50061, 33.94773, 41.87422…
$ county                <chr> "Lake County", "Big Horn County", "Orleans Parish", "Valencia County", "Salt Lake County", "DeKalb County", "Los Angeles County"…
$ census_tract_geoid    <dbl> 18089041700, 56003962800, 22071013400, 35061971100, 49035113309, 13089023702, 6037900607, 20173003900, 27137015800, 53033032605,…
$ narrative             <chr> "While conducting a welfare check at a Lake Station, IN, home, the police discovered the bodies of a man, a woman, a three child…

Filling in blank information for clarity.

unique(incidents$situation_type)
 [1] "Family issue"           "Despondency"            "Terrorism"              ""                       "Other"                  "Indiscriminate"        
 [7] "Undetermined"           "Robbery"                "Arson"                  "Interpersonal conflict" "Gang conflict"          "Drug trade"            
[13] "Employment grievance"   "Hate"                   "Profit"                
incidents$situation_type[incidents$situation_type == ""] <- "Unknown"

unique(incidents$situation_type)
 [1] "Family issue"           "Despondency"            "Terrorism"              "Unknown"                "Other"                  "Indiscriminate"        
 [7] "Undetermined"           "Robbery"                "Arson"                  "Interpersonal conflict" "Gang conflict"          "Drug trade"            
[13] "Employment grievance"   "Hate"                   "Profit"                

Here is a quick diagram of the US (Excluding Alaska and Hawaii as it’s not properly fitted) that showcases where all the incidents took place. The plan is for me to eventually also turn this into a heat map to give a better gauge as when there are multiple like this it is difficult to showcase how much there is due to overlap. I am also attempting two different ways of showing the data points. Eventually I’ll figure out which would be the most effective method.

varI <- incidents %>%
  select(c(incident_id, longitude, latitude, firstcod, situation_type, location, num_victims_killed, num_victims_injured)) %>%
  rename(lon = longitude) %>%
  rename(lat = latitude)

varI

transformedData <- usmap_transform(varI)
tempData <- transformedData %>%
  filter(row_number() != 262)

# Excluding the 2017 Las Vegas Nevada shooter incident as it gets in the way of clarity.
tempData <- transformedData %>%
  filter(row_number() != 262)

plot_usmap() + geom_sf(
  data = tempData,
  aes(color = firstcod, size = num_victims_injured),
  alpha = .5) +
  theme(legend.position = "right", legend.key.size = unit(1, 'mm'), plot.title=element_text(hjust=0.5, face = "bold")) +
  labs(title = "PRIMARY reason for injured victims", caption = "-The 2017 Las Vegas Shooter has been excluded for clarity. That event had 60 deaths and 867 injured.")

# Getting a count of the incidents per state
stateIncidents <- incidents %>%
  count(state)

# Setting a fill color variable dependent on the number of incidents
stateIncidents <- stateIncidents %>%
  mutate(fillColor = ifelse(n>=40, "40+", ifelse(n>=20, "20-39", "0-19")))

Here is a bar graph statistic showcasing all of the incidents from the data set and showing them from most to least in a bar graph form. From this we can see that California, Texas and Illinois are the three most dangerous (in the sense of the most mass murders occuring) locations in the US. Florida being an honorable mention for fourth place. From here on I could also then further test to check the amount of victims, whilst this graph shows the most mass killings in occurrence for the past ~20 years, that does not necessarily pertain to deadliness as there may be other states that have less but more severe mass murders.

# Getting a sum of the amount of both victims killed and injured per state
deadlynessPerState <- incidents %>%
  group_by(state) %>%
  summarize(totalDeaths = sum(num_victims_killed), totalInjured = sum(num_victims_injured))

# Now applying a manual fill variable to be able to apply to the graph later
deadlynessPerState <- deadlynessPerState %>%
  mutate(fillColorDeath = ifelse(totalDeaths>=200, "200+", ifelse(totalDeaths>=100, "100-199", "0-99")),
         fillColorInjury = ifelse(totalInjured>=200, "200+", ifelse(totalDeaths>=100, "100-199", "0-99")))

ggplot(weapons, aes(x = weapon_type)) +
  geom_bar(width = 0.8)

With this Violin plot (+ Boxplot) we can see the general distribution of the victims as well as the medians. Generally it seems that females and males are both on average not too dissimilar but this is without knowing the exact amount of numbers. Proportionally however, the age of which they died does not seem to matter despite the sex. The unknown seems to be cases which the bodies were recovered but maybe not able to be identified as these cases have little to no information to them

I specifically remember this occurrence, I recall it being everywhere on the news and so I tested out the ID and seeing how well I could extract the data.


incidentsFL <- incidents %>%
  filter(state == "FL")

incidentsFL

weapons %>%
  filter(incident_id == 300)

victims %>%
  filter(incident_id == 300)

offenders %>%
  filter(incident_id == 300)

Here I was checking if there are any other discriminatory attacks against gay people searching through the narratives for the term “gay”, surprisingly, out of all the ones in the US only one showed up, which is the same one from above that occurred in Orlando.

# With grepl I can search for specific words in the narrative or any other of the strings of texts that give narratives.
incidents %>%
  filter(grepl("gay", narrative))
NA

I would consider this data set to be relatively clean. But, there are some things that could be altered for the sake of working with them. For example, there are a lot of NULL cells but due to the type of data I do not plan to remove these rows, I will however probably change them into “N/A” or something of the sort as they are important. There are some features, for example - situation_type, which has “Undetermines” and just flat out Null. Reading through the narratives, my personal belief is that these are most likely incidents in which there was simply no official statement of what type of situation the incident was. This would be a situation in which I would alter the data to officially state something like “Not Stated”

Discussion:

My general impression on this data set is that while it is very interested, at least for me, it does pose the challenge of attempting to do any sort of predictions. A lot of this data is simply fact but because of the nature of the data I cannot seem to do any sort of time series data as the dates could be considered as continuous but the situations are separate and not linked together.

Regarding the type of models, I’m thinking of maybe using a type of linear regression model to see if maybe there is any correlation with certain features to determine/predetermine the potential lethality of a situation based on the information it has given us. This would be tricky as things like race and age are too broad and other features like situation_type are generally hard to gague before the situation has already escalated. That said I’ll attempt to at least see what I would be able to predict like maybe what the odds are of survival with specific body types (age, sex, race, etc.) have in their respective situations. This could be done with the usage of linear regression.

fullMergedClean <- subset(fullMerged, select = -c(incident_id, location_type, longitude, latitude, census_tract_geoid, narrative, firstname, lastname, middlename, suffix, deathcause, outcome, criminal_justice_process, sentence_type, sentence_details, victim_id, weapon_id, ))
Error in c(incident_id, location_type, longitude, latitude, census_tract_geoid,  : 
  argument 18 is empty
fullMergedClean$had_survivor <- ifelse(fullMergedClean$num_victims_injured > 0, "Yes", "No")
fullMergedClean$had_survivor <- as.factor(fullMergedClean$had_survivor)
non_numeric_cols
 [1] "weekday"               "state"                 "firstcod"              "secondcod"             "if_assault_rifle_used" "situation_type"       
 [7] "location"              "race.x"                "sex.x"                 "race.y"                "sex.y"                 "vorelationship"       
[13] "weapon_type"           "gun_class"             "gun_type"              "had_survivor"         
incidents
offenders
victims
weapons

FEATURE ENGINEER, GOING TO MERGE EVERYTHING Features wanted incident_id, date, state, num_offenders, num_victims_killed, num_victims_injured, firstcod, if_assault_rifle_used, situation_type, location, offender_age, offender_race, offender_sex, victim_age, victim_sex, vorelationship, weapon_type

ADD num_victims_total

Turn Categorical into dummy variables

# Fixing the date and adding extra date columns.
incidentsUpdated <- incidents %>%
  mutate(
    date = as.Date(date),
    year = year(date),
    month = month(date),
    day = day(date),
    weekday = weekdays(date)
  )

incidentsUpdated <- na.omit(incidentsUpdated)

incidentsUpdated$had_survivor <- ifelse(incidentsUpdated$num_victims_injured > 0, "Yes", "No")
incidentsUpdated$had_survivor <- as.factor(incidentsUpdated$had_survivor)

# [Name] Summaries, these will get all the columns that will be used for the models.
incidentsSum <- incidentsUpdated %>%
  subset(select = c(incident_id, date, year, month, day, weekday, state, num_offenders, num_victims_killed, num_victims_injured, firstcod, situation_type, location, had_survivor))

offenderSum <- offenders %>%
  subset(select = c(incident_id, offender_id, age, race, sex)) %>%
  rename(offenderAge = age) %>%
  rename(offenderRace = race) %>%
  rename(offenderSex = sex)

victimSum <- victims %>%
  subset(select = c(incident_id, victim_id, age, race, sex, vorelationship)) %>%
  rename(victimAge = age) %>%
  rename(victimRace = race) %>%
  rename(victimSex = sex)

weaponSum <- weapons %>%
  subset(select = c(incident_id, weapon_id, weapon_type))

# Merging the data
totalData <- incidentsSum %>%
  left_join(offenderSum, by = "incident_id") %>%
  left_join(victimSum, by = "incident_id") %>%
  left_join(weaponSum, by = "incident_id")
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
totalData <- na.omit(totalData)
totalData
charEncode <- c("weekday", "state", "firstcod", "situation_type", "location", "offenderRace", "offenderSex", "victimRace", "victimSex", "vorelationship", "weapon_type")
# Turns any blank cells to "Unknown"
totalData[totalData == ""] <- "Unknown"

#any(is.na(totalData))

# This will factorize all of the data instead of having to manually do it
totalDataFactored <- totalData %>%
  mutate(across(all_of(charEncode), ~ as.integer(factor(.x))))
# Sums the max value in all of the columns which should, in theory, give the total amount of distinct columns I should have.
sum(sapply(totalDataFactored[charEncode], max))
[1] 152
# Get all of the names and which column they were from
factorLookup <- totalData %>%
  dplyr::select(charEncode) %>%
  pivot_longer(cols = everything(),
               names_to = "Origin",
               values_to = "Name") 

# Gathers all of the factored data
factorLookup2 <- totalDataFactored %>%
  dplyr::select(charEncode) %>%
  pivot_longer(cols = everything(),
               names_to = NULL,
               values_to = "Encoded Value")

# Assuming nothing has changed and having checked the number it matches up. I assume this is correct and now factorLookup is a good dictionary to go back to.
factorLookup <- cbind(factorLookup, factorLookup2)
factorLookup <- distinct(factorLookup)
factorLookup
splitData <- createDataPartition(binaryFactored$had_survivor, p = 0.8, list = FALSE)
trainData <- binaryFactored[splitData, ]
testData <- binaryFactored[-splitData, ]
# This is so that I can use it in the corr matrix but also make my life easier
totalDataFactoredForReal <- subset(totalDataFactored, select = -c(incident_id, date, year, month, offender_id, victim_id))
totalDataFactoredForReal$had_survivor <- as.numeric(totalDataFactoredForReal$had_survivor)

# Linear Regression Model
linModel <- lm(had_survivor ~ ., data = totalDataFactoredForReal)
summary(logModel)

Call:
lm(formula = had_survivor ~ ., data = totalDataFactoredForReal)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.97413 -0.34860 -0.00111  0.30482  0.84998 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)          1.705e+00  5.937e-02  28.724  < 2e-16 ***
day                 -4.216e-03  5.544e-04  -7.604 3.19e-14 ***
weekday              1.154e-02  2.514e-03   4.592 4.46e-06 ***
state               -4.960e-04  3.283e-04  -1.511  0.13087    
num_offenders       -8.142e-03  3.775e-03  -2.157  0.03105 *  
num_victims_killed   1.323e-02  7.730e-04  17.118  < 2e-16 ***
num_victims_injured -2.157e-04  5.540e-05  -3.893 9.97e-05 ***
firstcod             2.365e-02  5.646e-03   4.188 2.84e-05 ***
situation_type       1.169e-03  1.360e-03   0.859  0.39018    
location            -4.773e-02  1.499e-03 -31.844  < 2e-16 ***
offenderAge         -2.790e-03  4.852e-04  -5.749 9.30e-09 ***
offenderRace         1.070e-03  2.895e-03   0.370  0.71176    
offenderSex          1.233e-01  1.678e-02   7.350 2.17e-13 ***
victimAge            1.138e-03  2.484e-04   4.581 4.70e-06 ***
victimRace          -2.621e-03  2.783e-03  -0.942  0.34631    
victimSex           -1.343e-02  8.880e-03  -1.513  0.13038    
vorelationship      -1.126e-04  5.381e-04  -0.209  0.83425    
weapon_id           -5.213e-05  1.702e-05  -3.062  0.00221 ** 
weapon_type         -1.947e-02  4.101e-03  -4.748 2.09e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3851 on 8108 degrees of freedom
Multiple R-squared:  0.3731,    Adjusted R-squared:  0.3718 
F-statistic: 268.1 on 18 and 8108 DF,  p-value: < 2.2e-16
corM <- cor(totalDataFactoredForReal)
corrplot(corM, method = "color")

heatmap(corM, symm = TRUE, main = "Correlation Heatmap")

library(randomForest)
Warning: package ‘randomForest’ was built under R version 4.4.3randomForest 4.7-1.2
Type rfNews() to see new features/changes/bug fixes.

Attaching package: ‘randomForest’

The following object is masked from ‘package:dplyr’:

    combine

The following object is masked from ‘package:ggplot2’:

    margin
logModel <- glm(had_survivor~ . -num_victims_killed -num_victims_injured -had_survivor, data = trainData, family = "binomial")
stepLogModel <- step(logModel, direction = "both")
Start:  AIC=6468.25
had_survivor ~ (day + weekday + state + num_offenders + num_victims_killed + 
    num_victims_injured + firstcod + situation_type + location + 
    offenderAge + offenderRace + offenderSex + victimAge + victimRace + 
    victimSex + vorelationship + weapon_id + weapon_type) - num_victims_killed - 
    num_victims_injured - had_survivor

                 Df Deviance    AIC
- victimAge       1   6434.3 6466.3
<none>                6434.2 6468.2
- firstcod        1   6436.6 6468.6
- victimRace      1   6436.7 6468.7
- weekday         1   6437.3 6469.3
- state           1   6437.4 6469.4
- victimSex       1   6439.0 6471.0
- situation_type  1   6439.1 6471.1
- num_offenders   1   6442.7 6474.7
- weapon_id       1   6446.1 6478.1
- weapon_type     1   6450.8 6482.8
- offenderRace    1   6452.2 6484.2
- vorelationship  1   6463.9 6495.9
- offenderSex     1   6487.0 6519.0
- offenderAge     1   6489.8 6521.8
- day             1   6718.2 6750.2
- location        1   7431.4 7463.4

Step:  AIC=6466.3
had_survivor ~ day + weekday + state + num_offenders + firstcod + 
    situation_type + location + offenderAge + offenderRace + 
    offenderSex + victimRace + victimSex + vorelationship + weapon_id + 
    weapon_type

                 Df Deviance    AIC
<none>                6434.3 6466.3
- firstcod        1   6436.7 6466.7
- victimRace      1   6436.9 6466.9
- weekday         1   6437.3 6467.3
- state           1   6437.4 6467.4
+ victimAge       1   6434.2 6468.2
- victimSex       1   6439.0 6469.0
- situation_type  1   6439.2 6469.2
- num_offenders   1   6442.7 6472.7
- weapon_id       1   6446.4 6476.4
- weapon_type     1   6450.8 6480.8
- offenderRace    1   6452.4 6482.4
- vorelationship  1   6464.8 6494.8
- offenderSex     1   6487.2 6517.2
- offenderAge     1   6490.5 6520.5
- day             1   6719.1 6749.1
- location        1   7489.2 7519.2
stepLogModelCV <- train(had_survivor ~ . -num_victims_killed -num_victims_injured -had_survivor, data = trainData, method = "glm", family = "binomial", trControl = train_control, metric = "ROC")
stepLogPred <- predict(stepLogModelCV, newdata = testData, type = "prob")
rocOb <- roc(testData$had_survivor, stepLogPred$Yes)
Setting levels: control = Yes, case = No
Setting direction: controls > cases
plot(rocOb, print.auc = TRUE,
         col = "blue",
         lwd = 3,
         legacy.axes = TRUE,
         main = "ROC Curve for Survival Prediction")
abline(a = 0, b = 1, lty = 2, col = "red")

auc(rocOb)
Area under the curve: 0.8204
LS0tDQp0aXRsZTogIlByb2plY3Q6IE1hc3MgTXVyZGVyIFN0YXRpc3RpY3MiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogIkhhbGFuIEJhZGlsbGEgT3NvcmlvIg0KLS0tDQoNCmBgYHtyfQ0KU3lzLnNldGVudihMQU5HID0gImVuIikNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShtYXBzKQ0KbGlicmFyeShtYXBwcm9qKQ0KbGlicmFyeSh1c21hcCkNCmxpYnJhcnkoSVNMUjIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQpsaWJyYXJ5KE1BU1MpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShoZXhiaW4pDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkocFJPQykNCmBgYA0KDQpgYGB7cn0NCnNldHdkKCJDOi9Vc2Vycy9oYWxhbi9EZXNrdG9wL0ZQVS9TUHJpbmcyMDI1L1N0YXRpc3RpY2FsTGVhcm5pbmciKQ0KDQppbmNpZGVudHMgPC0gcmVhZC5jc3YoIm1hc3Nfa2lsbGluZ19pbmNpZGVudHNfcHVibGljLmNzdiIpDQpvZmZlbmRlcnMgPC0gcmVhZC5jc3YoIm1hc3Nfa2lsbGluZ19vZmZlbmRlcnNfcHVibGljLmNzdiIpDQp2aWN0aW1zIDwtIHJlYWQuY3N2KCJtYXNzX2tpbGxpbmdfdmljdGltc19wdWJsaWMuY3N2IikNCndlYXBvbnMgPC0gcmVhZC5jc3YoIm1hc3Nfa2lsbGluZ193ZWFwb25zX3B1YmxpYy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KdmlldyhpbmNpZGVudHMpDQp2aWV3KG9mZmVuZGVycykNCnZpZXcodmljdGltcykNCnZpZXcod2VhcG9ucykNCmBgYA0KDQpEQVRBOiBNYXNzIEtpbGxpbmcgRGF0YWJhc2UgMjAwNiAtIFByZXNlbnQgKEFTIE9GIE1BUkNIIDE1dGggMjAyNSkNCg0KTWFzcyBNdXJkZXIgZm9yIHRoaXMgZGF0YXNldCBpcyBkZWZpbmVkIGFzICIuLi50aGUgaW50ZW50aW9uYWwga2lsbGluZyBvZiBmb3VyIG9yIG1vcmUgdmljdGltcyBieSBhbnkgbWVhbnMgd2l0aGluIGEgMjQtaG91ciBwZXJpb2QsIGV4Y2x1ZGluZyB0aGUgZGVhdGggb2YgdW5ib3JuIGNoaWxkcmVuIGFuZCB0aGUgb2ZmZW5kZXIocykuKQ0KDQpUaGlzIERhdGFiYXNlIGlzIGNvbnN0YW50bHkgYmVpbmcgdXBkYXRlZCAoV2FzIGp1c3QgdXBkYXRlZCAyIGhvdXJzIGFnbyBhcyBvZiBNYXJjaCAxNnRoLCAyMDI1IGF0IDEyOjUwUE0pIGFuZCBJIHdvdWxkIHNheSBpcyB2ZXJ5IHRydXN0d29ydGh5LiBUaGUgY29udGFjdCBkZXRhaWxzIGZvciB0aGlzIERhdGFiYXNlIGlzIEp1c3RpbiBNeWVycy4gSnVzdGluIE15ZXJzIGlzIGEgdmV0ZXJhbiBqb3VybmFsaXN0IGZvciAiVGhlIEFzc29jaWF0ZWQgUHJlc3MiLCB0aGV5IGFyZSBhbHNvIHdvcmtpbmcgd2l0aCBOb3J0aGVhc3Rlcm4gVW5pdmVyc2l0eSdzIEphbWVzIEFsYW4gRm94IHdob20gaXMgYWxzbyBhIGNvbnRhY3QuDQoNCg0KDQpGZWF0dXJlcyhJbmNpZGVudCBbMTkgQ29sdW1ucyB4IDYxNSBSb3dzXSk6DQoNCjxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPlJlZDwvc3Bhbj4gPSBDYXRlZ29yaWNhbCB8IDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+R3JlZW48L3NwYW4+ID0gTnVtZXJpY2FsDQoNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+aW5jaWRlbnRfaWQ8L3NwYW4+OiBJRCBvZiBpbmNpZGVudA0KLSA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPmRhdGU8L3NwYW4+OiBEYXRlIG9mIGluY2lkZW50DQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPmNpdHk8L3NwYW4+OiBDaXR5L1Rvd24gbmFtZQ0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5zdGF0ZTwvc3Bhbj46IFN0YXRlIHBvc3RhbCBjb2RlDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+bnVtX29mZmVuZGVyczwvc3Bhbj46IE51bWJlciBvZiBvZmZlbmRlcnMNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj5udW1fa2lsbGVkPC9zcGFuPjogTnVtYmVyIG9mIHZpY3RpbXMga2lsbGVkDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+bnVtX2luanVyZWQ8L3NwYW4+OiBOdW1uYmVyIG9mIHZpY3RpbXMgaW5qdXJlZA0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5maXJzdGNvZDwvc3Bhbj46IEZpcnN0IGNhdXNlIG9mIGRlYXRoDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnNlY29uZGNvZDwvc3Bhbj46IFNlY29uZCBjYXVzZSBvZiBkZWF0aA0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5pZl9hc3NhdWx0X3JpZmxlPC9zcGFuPjogV2hldGhlciBhIHJpZmxlIHdhcyB1c2VkDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnR5cGU8L3NwYW4+OiBUeXBlIG9mIGluY2lkZW50IChpZS4gRmFtaWx5LCBGZWxvbnksIFB1YmxpYywgZXRjLikNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+c2l0dWF0aW9uX3R5cGU8L3NwYW4+OiBUeXBlIG9mIHNpdHVhdGlvbiAoaWUuIEFyc29uLCBEcnVnIFRyYWRlLCBGYW1pbHkgSXNzdWUsIGV0Yy4pDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPmxvY2F0aW9uX3R5cGU8L3NwYW4+OiBUeXBlIG9mIGxvY2F0aW9uIChpZS4gQ29tbWVyY2lhbC9SZXRhaWwvRW50ZXJ0YWlubWVudCwgR292ZXJubWVudC9UcmFuc2l0LCBIb3VzZSBvZiB3b3JzaGlwLCBldGMuKQ0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5sb2NhdGlvbjwvc3Bhbj46IExvY2F0aW9uIChpZS4gQmFyL0NsdWIvUmVzdGF1cmFudCwgQ29sbGVnZSwgQ29tbWVyY2lhbC9SZXRhaWwsIGV0Yy4pDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+bG9uZ2l0dWRlPC9zcGFuPg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPmxhdGl0dWRlPC9zcGFuPg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5jb3VudHk8L3NwYW4+OiBDb3VudHkgYXNzb2NpYXRlZCB3aXRoIGNvb3JkaW5hdGVzDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpncmVlbiI+Z2VvY29kZTwvc3Bhbj46IEZJUFMgZ2VvY29kZSBvZiBsb2NhdGlvbg0KLSBuYXJyYXRpdmU6IERlc2NyaXB0aW9uIG9mIGluY2lkZW50DQoNCg0KRmVhdHVyZXMoVmljdGltcyBbNiBDb2x1bW5zIHggMzE3NCBSb3dzXSk6DQoNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+aW5jaWRlbnRfaWQ8L3NwYW4+OiBJRCBmb3IgaW5jaWRlbnQsIHVzZWQgdG8gbGluayB0aGUgdmljdGltcyBkYXRhLg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj52aWN0aW1faWQ8L3NwYW4+OiBJRCBvZiB0aGUgdmljdGltLCBmb3IgcmVjb3JkaW5nIHB1cnBvc2VzLg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6Z3JlZW4iPmFnZTwvc3Bhbj46IEFnZSBvZiB0aGUgdmljdGltLg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5yYWNlPC9zcGFuPg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5zZXg8L3NwYW4+DQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnZvcmVsYXRpb25zaGlwPC9zcGFuPjogUmVsYXRpb25zaGlwIG9mIHZpY3RpbSB0byB0aGUgb2ZmZW5kZXINCg0KRmVhdHVyZXMoV2VhcG9ucyBbNSBDb2x1bW5zIHggOTYzIFJvd3NdKToNCg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5pbmNpZGVudF9pZDwvc3Bhbj46IElEIGZvciBpbmNpZGVudCwgdXNlZCB0byBsaW5rIHdlYXBvbiB0byBpbmNpZGVudC4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+d2VhcG9uX2lkPC9zcGFuPjogSUQgZm9yIHdlYXBvbiBpZGVudGlmaWNhdGlvbi4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+d2VhcG9uX3R5cGU8L3NwYW4+OiBUeXBlIG9mIHdlYXBvbi4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+Z3VuX2NsYXNzPC9zcGFuPjogQ2xhc3NpZmljYXRpb24gb2YgZ3VuIChIYW5kZ3VuLCBMb25nIGd1biwgVW5rbm93biBndW4gY2xhc3MsIE5vbi1ndW4pDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPmd1bl90eXBlPC9zcGFuPjogVHlwZSBvZiBndW4gKGllLiBIYW5kZ3VuLCBQaXN0b2wsIFJldm9sdmVyLCBSaWZsZSwgZXRjLikNCg0KRmVhdHVyZXMoT2ZmZW5kZXJzIFsxNSBDb2x1bW5zIHggNzg4IFJvd3NdKToNCg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5pbmNpZGVudF9pZDwvc3Bhbj46IElEIG9mIGluY2lkZW50LCB1c2VkIHRvIGNvbm5lY3QgdGhlIDQgZGF0YSBzZXRzLg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5vZmZlbmRlcl9pZDwvc3Bhbj46IElEIHVzZWQgZm9yIGNsYXNzaWZpY2F0aW9uIG9mIG9mZmVuZGVycyAoTW9zdGx5IGZvciBtdWx0aS1vZmZlbmRlciBpbmNpZGVudHMpDQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPmZpcnN0bmFtZTwvc3Bhbj4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+bWlkZGxlbmFtZTwvc3Bhbj4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+bGFzdG5hbWU8L3NwYW4+DQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnN1ZmZpeDwvc3Bhbj4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOmdyZWVuIj5hZ2U8L3NwYW4+DQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnJhY2U8L3NwYW4+DQotIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnNleDwvc3Bhbj4NCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+c3VpY2lkZTwvc3Bhbj46IFdoZXRoZXIgdGhlIG9mZmVuZGVyIGNvbW1pdGVkIHN1aWNpZGUgb3Igbm90Lg0KLSA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj5kZWF0aGNhdXNlPC9zcGFuPjogV2hhdCBkaWQgdGhlIG9mZmVuZGVyIGRpZSBieSB3aGV0aGVyIGl0IHdhcyBzdWljaWRlIG9yIGtpbGxlZCBieSBhIGJ5c3RhbmRlci9wb2xpY2UNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+b3V0Y29tZTwvc3Bhbj46IFdoYXQgd2FzIHRoZSBsZWdhbCBvdXRjb21lIGZvciB0aGUgaW5jaWRlbnQgKGllLiBBY3F1aXR0ZWQsIEFycmVzdGVkL1BlbmRpbmcgdHJpYWwsIENoYXJnZXMgZHJvcHBlZCwgZXRjLikNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+Y3JpbWluYWxfanVzdGljZV9wcm9jZXNzPC9zcGFuPjogU3RhdHVzIG9mIGNyaW1pbmFsIGp1c3RpY2UgcHJvY2VzcyAoaWUuIEFycmVzdGVkL1BlbmRpbmcgdHJpYWwsIGNoYXJnZXMgZHJvcHBlZCwgZXRjLikNCi0gPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+c2VudGVuY2VfdHlwZTwvc3Bhbj46IFR5cGUgb2Ygc2VudGVuY2UgY29udmljdGVkIChpZS4gQWNxdWl0dGVkLCBBd2FpdGluZyBzZW50ZW5jaW5nLCBDb21taXR0ZWQsIERlYXRoIHNlbnRlbmNlIGV0Yy4pDQotIHNlbnRlbmNlX2RldGFpbHM6IERldGFpbHMgYWJvdXQgdGhlIHNlbnRlbmNlIGNvbnZpY3RlZC4NCg0KYGBge3J9DQpnbGltcHNlKGluY2lkZW50cykNCmBgYA0KDQoNCkZpbGxpbmcgaW4gYmxhbmsgaW5mb3JtYXRpb24gZm9yIGNsYXJpdHkuDQoNCmBgYHtyfQ0KdW5pcXVlKGluY2lkZW50cyRzaXR1YXRpb25fdHlwZSkNCg0KaW5jaWRlbnRzJHNpdHVhdGlvbl90eXBlW2luY2lkZW50cyRzaXR1YXRpb25fdHlwZSA9PSAiIl0gPC0gIlVua25vd24iDQoNCnVuaXF1ZShpbmNpZGVudHMkc2l0dWF0aW9uX3R5cGUpDQpgYGANCkhlcmUgaXMgYSBxdWljayBkaWFncmFtIG9mIHRoZSBVUyAoRXhjbHVkaW5nIEFsYXNrYSBhbmQgSGF3YWlpIGFzIGl0J3Mgbm90IHByb3Blcmx5IGZpdHRlZCkgdGhhdCBzaG93Y2FzZXMgd2hlcmUgYWxsIHRoZSBpbmNpZGVudHMgdG9vayBwbGFjZS4gVGhlIHBsYW4gaXMgZm9yIG1lIHRvIGV2ZW50dWFsbHkgYWxzbyB0dXJuIHRoaXMgaW50byBhIGhlYXQgbWFwIHRvIGdpdmUgYSBiZXR0ZXIgZ2F1Z2UgYXMgd2hlbiB0aGVyZSBhcmUgbXVsdGlwbGUgbGlrZSB0aGlzIGl0IGlzIGRpZmZpY3VsdCB0byBzaG93Y2FzZSBob3cgbXVjaCB0aGVyZSBpcyBkdWUgdG8gb3ZlcmxhcC4gSSBhbSBhbHNvIGF0dGVtcHRpbmcgdHdvIGRpZmZlcmVudCB3YXlzIG9mIHNob3dpbmcgdGhlIGRhdGEgcG9pbnRzLiBFdmVudHVhbGx5IEknbGwgZmlndXJlIG91dCB3aGljaCB3b3VsZCBiZSB0aGUgbW9zdCBlZmZlY3RpdmUgbWV0aG9kLg0KYGBge3J9DQp2YXJJIDwtIGluY2lkZW50cyAlPiUNCiAgc2VsZWN0KGMoaW5jaWRlbnRfaWQsIGxvbmdpdHVkZSwgbGF0aXR1ZGUsIGZpcnN0Y29kLCBzaXR1YXRpb25fdHlwZSwgbG9jYXRpb24sIG51bV92aWN0aW1zX2tpbGxlZCwgbnVtX3ZpY3RpbXNfaW5qdXJlZCkpICU+JQ0KICByZW5hbWUobG9uID0gbG9uZ2l0dWRlKSAlPiUNCiAgcmVuYW1lKGxhdCA9IGxhdGl0dWRlKQ0KDQp2YXJJDQoNCnRyYW5zZm9ybWVkRGF0YSA8LSB1c21hcF90cmFuc2Zvcm0odmFySSkNCmBgYA0KDQoNCmBgYHtyfQ0KdGVtcERhdGEgPC0gdHJhbnNmb3JtZWREYXRhICU+JQ0KICBmaWx0ZXIocm93X251bWJlcigpICE9IDI2MikNCmBgYA0KDQpgYGB7cn0NCnBsb3RfdXNtYXAoKSArIGdlb21fc2YoDQogIGRhdGEgPSB0ZW1wRGF0YSwNCiAgYWVzKGNvbG9yID0gZmlyc3Rjb2QsIHNpemUgPSBudW1fdmljdGltc19raWxsZWQpLA0KICBhbHBoYSA9IC41KSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwgJ21tJyksIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgZmFjZSA9ICJib2xkIikpICsNCiAgbGFicyh0aXRsZSA9ICJQUklNQVJZIHJlYXNvbiBmb3IgdmljdGltIGRlYXRocyIsIGNhcHRpb24gPSAiLVRoZSAyMDE3IExhcyBWZWdhcyBTaG9vdGVyIGhhcyBiZWVuIGV4Y2x1ZGVkIGZvciBjbGFyaXR5LiBUaGF0IGV2ZW50IGhhZCA2MCBkZWF0aHMgYW5kIDg2NyBpbmp1cmVkLiIpDQoNCmBgYA0KDQpgYGB7cn0NCnBsb3RfdXNtYXAoKSArIGdlb21fc2YoDQogIGRhdGEgPSB0ZW1wRGF0YSwNCiAgYWVzKGNvbG9yID0gZmlyc3Rjb2QsIHNpemUgPSBudW1fdmljdGltc19pbmp1cmVkKSwNCiAgYWxwaGEgPSAuNSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDEsICdtbScpLCBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIGZhY2UgPSAiYm9sZCIpKSArDQogIGxhYnModGl0bGUgPSAiUFJJTUFSWSByZWFzb24gZm9yIGluanVyZWQgdmljdGltcyIsIGNhcHRpb24gPSAiLVRoZSAyMDE3IExhcyBWZWdhcyBTaG9vdGVyIGhhcyBiZWVuIGV4Y2x1ZGVkIGZvciBjbGFyaXR5LiBUaGF0IGV2ZW50IGhhZCA2MCBkZWF0aHMgYW5kIDg2NyBpbmp1cmVkLiIpDQoNCmBgYA0KDQpgYGB7cn0NCnBsb3RfdXNtYXAoKSArIGdlb21fc2YoDQogIGRhdGEgPSB0ZW1wRGF0YSwNCiAgYWVzKGNvbG9yID0gbG9jYXRpb24sIHNpemUgPSBudW1fdmljdGltc19raWxsZWQpLA0KICBhbHBoYSA9IC41KSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwgJ21tJyksIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgZmFjZSA9ICJib2xkIikpICsNCiAgbGFicyh0aXRsZSA9ICJMb2NhdGlvbiBhbmQgQW1vdW50IG9mIFZpY3RpbSBEZWF0aHMiLCBjYXB0aW9uID0gIi1UaGUgMjAxNyBMYXMgVmVnYXMgU2hvb3RlciBoYXMgYmVlbiBleGNsdWRlZCBmb3IgY2xhcml0eS4gVGhhdCBldmVudCBoYWQgNjAgZGVhdGhzIGFuZCA4NjcgaW5qdXJlZC4iKQ0KYGBgDQpgYGB7cn0NCnBsb3RfdXNtYXAoKSArIGdlb21fc2YoDQogIGRhdGEgPSB0ZW1wRGF0YSwNCiAgYWVzKGNvbG9yID0gbG9jYXRpb24sIHNpemUgPSBudW1fdmljdGltc19pbmp1cmVkKSwNCiAgYWxwaGEgPSAuNSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDEsICdtbScpLCBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIGZhY2UgPSAiYm9sZCIpKSArDQogIGxhYnModGl0bGUgPSAiTG9jYXRpb24gYW5kIEFtb3VudCBvZiBJbmp1cmVkIFZpY3RpbXMiLCBjYXB0aW9uID0gIi1UaGUgMjAxNyBMYXMgVmVnYXMgU2hvb3RlciBoYXMgYmVlbiBleGNsdWRlZCBmb3IgY2xhcml0eS4gVGhhdCBldmVudCBoYWQgNjAgZGVhdGhzIGFuZCA4NjcgaW5qdXJlZC4iKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdF91c21hcCgpICsgZ2VvbV9zZigNCiAgZGF0YSA9IHRlbXBEYXRhLA0KICBhZXMoY29sb3IgPSBzaXR1YXRpb25fdHlwZSwgc2l6ZSA9IG51bV92aWN0aW1zX2tpbGxlZCksDQogIGFscGhhID0gMC44KSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwgJ21tJyksIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgZmFjZSA9ICJib2xkIikpICsNCiAgbGFicyh0aXRsZSA9ICJUeXBlIG9mIEluY2lkZW50IGFuZCBBbW91bnQgb2YgVmljdGltIERlYXRocyIsIGNhcHRpb24gPSAiLVRoZSAyMDE3IExhcyBWZWdhcyBTaG9vdGVyIGhhcyBiZWVuIGV4Y2x1ZGVkIGZvciBjbGFyaXR5LiBUaGF0IGV2ZW50IGhhZCA2MCBkZWF0aHMgYW5kIDg2NyBpbmp1cmVkLiIpDQoNCmBgYA0KYGBge3J9DQpwbG90X3VzbWFwKCkgKyBnZW9tX3NmKA0KICBkYXRhID0gdGVtcERhdGEsDQogIGFlcyhjb2xvciA9IHNpdHVhdGlvbl90eXBlLCBzaXplID0gbnVtX3ZpY3RpbXNfaW5qdXJlZCksDQogIGFscGhhID0gMC44KSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMSwgJ21tJyksIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgZmFjZSA9ICJib2xkIikpICsNCiAgbGFicyh0aXRsZSA9ICJUeXBlIG9mIEluY2lkZW50IGFuZCBBbW91bnQgb2YgSW5qdXJlZCBWaWN0aW1zIiwgY2FwdGlvbiA9ICItVGhlIDIwMTcgTGFzIFZlZ2FzIFNob290ZXIgaGFzIGJlZW4gZXhjbHVkZWQgZm9yIGNsYXJpdHkuIFRoYXQgZXZlbnQgaGFkIDYwIGRlYXRocyBhbmQgODY3IGluanVyZWQuIikNCg0KYGBgDQoNCmBgYHtyfQ0KIyBHZXR0aW5nIGEgY291bnQgb2YgdGhlIGluY2lkZW50cyBwZXIgc3RhdGUNCnN0YXRlSW5jaWRlbnRzIDwtIGluY2lkZW50cyAlPiUNCiAgY291bnQoc3RhdGUpDQoNCiMgU2V0dGluZyBhIGZpbGwgY29sb3IgdmFyaWFibGUgZGVwZW5kZW50IG9uIHRoZSBudW1iZXIgb2YgaW5jaWRlbnRzDQpzdGF0ZUluY2lkZW50cyA8LSBzdGF0ZUluY2lkZW50cyAlPiUNCiAgbXV0YXRlKGZpbGxDb2xvciA9IGlmZWxzZShuPj00MCwgIjQwKyIsIGlmZWxzZShuPj0yMCwgIjIwLTM5IiwgIjAtMTkiKSkpDQpgYGANCg0KSGVyZSBpcyBhIGJhciBncmFwaCBzdGF0aXN0aWMgc2hvd2Nhc2luZyBhbGwgb2YgdGhlIGluY2lkZW50cyBmcm9tIHRoZSBkYXRhIHNldCBhbmQgc2hvd2luZyB0aGVtIGZyb20gbW9zdCB0byBsZWFzdCBpbiBhIGJhciBncmFwaCBmb3JtLiBGcm9tIHRoaXMgd2UgY2FuIHNlZSB0aGF0IENhbGlmb3JuaWEsIFRleGFzIGFuZCBJbGxpbm9pcyBhcmUgdGhlIHRocmVlIG1vc3QgZGFuZ2Vyb3VzIChpbiB0aGUgc2Vuc2Ugb2YgdGhlIG1vc3QgbWFzcyBtdXJkZXJzIG9jY3VyaW5nKSBsb2NhdGlvbnMgaW4gdGhlIFVTLiBGbG9yaWRhIGJlaW5nIGFuIGhvbm9yYWJsZSBtZW50aW9uIGZvciBmb3VydGggcGxhY2UuIEZyb20gaGVyZSBvbiBJIGNvdWxkIGFsc28gdGhlbiBmdXJ0aGVyIHRlc3QgdG8gY2hlY2sgdGhlIGFtb3VudCBvZiB2aWN0aW1zLCB3aGlsc3QgdGhpcyBncmFwaCBzaG93cyB0aGUgbW9zdCBtYXNzIGtpbGxpbmdzIGluIG9jY3VycmVuY2UgZm9yIHRoZSBwYXN0IH4yMCB5ZWFycywgdGhhdCBkb2VzIG5vdCBuZWNlc3NhcmlseSBwZXJ0YWluIHRvIGRlYWRsaW5lc3MgYXMgdGhlcmUgbWF5IGJlIG90aGVyIHN0YXRlcyB0aGF0IGhhdmUgbGVzcyBidXQgbW9yZSBzZXZlcmUgbWFzcyBtdXJkZXJzLg0KDQpgYGB7cn0NCiMgQ3JlYXRpbmcgdGhlIGdyYXBoIGFuZCBvcmdhbml6aW5nIGJ5IGFtb3VudA0KZ2dwbG90KHN0YXRlSW5jaWRlbnRzLCBhZXMoeCA9IHJlb3JkZXIoc3RhdGUsICtuKSwgeSA9IG4sIGZpbGwgPSBmaWxsQ29sb3IpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5Jywgd2lkdGggPSAwLjgpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjAtMTkiID0gImdvbGQiLCAiMjAtMzkiID0gIm9yYW5nZSIsICI0MCsiID0gImRhcmtyZWQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNywgaGp1c3QgPSAxKSkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIEluY2lkZW50cyBQZXIgU3RhdGUiLCB4ID0gIlN0YXRlcyIsIHkgPSAiQW1vdW50IG9mIEluY2lkZW50cyIpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCmBgYHtyfQ0KIyBHZXR0aW5nIGEgc3VtIG9mIHRoZSBhbW91bnQgb2YgYm90aCB2aWN0aW1zIGtpbGxlZCBhbmQgaW5qdXJlZCBwZXIgc3RhdGUNCmRlYWRseW5lc3NQZXJTdGF0ZSA8LSBpbmNpZGVudHMgJT4lDQogIGdyb3VwX2J5KHN0YXRlKSAlPiUNCiAgc3VtbWFyaXplKHRvdGFsRGVhdGhzID0gc3VtKG51bV92aWN0aW1zX2tpbGxlZCksIHRvdGFsSW5qdXJlZCA9IHN1bShudW1fdmljdGltc19pbmp1cmVkKSkNCg0KIyBOb3cgYXBwbHlpbmcgYSBtYW51YWwgZmlsbCB2YXJpYWJsZSB0byBiZSBhYmxlIHRvIGFwcGx5IHRvIHRoZSBncmFwaCBsYXRlcg0KZGVhZGx5bmVzc1BlclN0YXRlIDwtIGRlYWRseW5lc3NQZXJTdGF0ZSAlPiUNCiAgbXV0YXRlKGZpbGxDb2xvckRlYXRoID0gaWZlbHNlKHRvdGFsRGVhdGhzPj0yMDAsICIyMDArIiwgaWZlbHNlKHRvdGFsRGVhdGhzPj0xMDAsICIxMDAtMTk5IiwgIjAtOTkiKSksDQogICAgICAgICBmaWxsQ29sb3JJbmp1cnkgPSBpZmVsc2UodG90YWxJbmp1cmVkPj0yMDAsICIyMDArIiwgaWZlbHNlKHRvdGFsSW5qdXJlZD49MTAwLCAiMTAwLTE5OSIsICIwLTk5IikpKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRlYWRseW5lc3NQZXJTdGF0ZSwgYWVzKHggPSByZW9yZGVyKHN0YXRlLCArdG90YWxEZWF0aHMpLCB5ID0gdG90YWxEZWF0aHMsIGZpbGwgPSBmaWxsQ29sb3JEZWF0aCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHdpZHRoID0gMC44KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjAtOTkiID0gImdvbGQiLCAiMTAwLTE5OSIgPSAib3JhbmdlIiwgIjIwMCsiID0gImRhcmtyZWQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNywgaGp1c3QgPSAxKSkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFZpY3RpbSBEZWF0aHMgUGVyIFN0YXRlIiwgeCA9ICJTdGF0ZXMiLCB5ID0gIkFtb3VudCBvZiBEZWF0aHMiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRlYWRseW5lc3NQZXJTdGF0ZSwgYWVzKHggPSByZW9yZGVyKHN0YXRlLCArdG90YWxJbmp1cmVkKSwgeSA9IHRvdGFsSW5qdXJlZCwgZmlsbCA9IGZpbGxDb2xvckluanVyeSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHdpZHRoID0gMC44KSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjAtOTkiID0gImdvbGQiLCAiMTAwLTE5OSIgPSAib3JhbmdlIiwgIjIwMCsiID0gImRhcmtyZWQiKSkgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNywgaGp1c3QgPSAxKSkgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIEluanVyZWQgVmljdGltcyBQZXIgU3RhdGUiLCB4ID0gIlN0YXRlcyIsIHkgPSAiQW1vdW50IG9mIEluanVyZWQiKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3Qod2VhcG9ucywgYWVzKHggPSB3ZWFwb25fdHlwZSkpICsNCiAgZ2VvbV9iYXIod2lkdGggPSAwLjgpDQpgYGANCg0KV2l0aCB0aGlzIFZpb2xpbiBwbG90ICgrIEJveHBsb3QpIHdlIGNhbiBzZWUgdGhlIGdlbmVyYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSB2aWN0aW1zIGFzIHdlbGwgYXMgdGhlIG1lZGlhbnMuIEdlbmVyYWxseSBpdCBzZWVtcyB0aGF0IGZlbWFsZXMgYW5kIG1hbGVzIGFyZSBib3RoIG9uIGF2ZXJhZ2Ugbm90IHRvbyBkaXNzaW1pbGFyIGJ1dCB0aGlzIGlzIHdpdGhvdXQga25vd2luZyB0aGUgZXhhY3QgYW1vdW50IG9mIG51bWJlcnMuIFByb3BvcnRpb25hbGx5IGhvd2V2ZXIsIHRoZSBhZ2Ugb2Ygd2hpY2ggdGhleSBkaWVkIGRvZXMgbm90IHNlZW0gdG8gbWF0dGVyIGRlc3BpdGUgdGhlIHNleC4gVGhlIHVua25vd24gc2VlbXMgdG8gYmUgY2FzZXMgd2hpY2ggdGhlIGJvZGllcyB3ZXJlIHJlY292ZXJlZCBidXQgbWF5YmUgbm90IGFibGUgdG8gYmUgaWRlbnRpZmllZCBhcyB0aGVzZSBjYXNlcyBoYXZlIGxpdHRsZSB0byBubyBpbmZvcm1hdGlvbiB0byB0aGVtDQoNCmBgYHtyfQ0KZ2dwbG90KHZpY3RpbXMsIGFlcyh4ID0gc2V4LCB5ID0gYWdlLCBmaWxsID0gc2V4KSkgKw0KICBnZW9tX3Zpb2xpbih0cmltID0gRkFMU0UpICsNCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC40KSArDQogIGdlb21faml0dGVyKHNoYXBlID0gMTYsIHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcigwLjEpLCBhbHBoYSA9IDAuMikNCmBgYA0KDQoNCkkgc3BlY2lmaWNhbGx5IHJlbWVtYmVyIHRoaXMgb2NjdXJyZW5jZSwgSSByZWNhbGwgaXQgYmVpbmcgZXZlcnl3aGVyZSBvbiB0aGUgbmV3cyBhbmQgc28gSSB0ZXN0ZWQgb3V0IHRoZSBJRCBhbmQgc2VlaW5nIGhvdyB3ZWxsIEkgY291bGQgZXh0cmFjdCB0aGUgZGF0YS4gDQoNCmBgYHtyfQ0KDQppbmNpZGVudHNGTCA8LSBpbmNpZGVudHMgJT4lDQogIGZpbHRlcihzdGF0ZSA9PSAiRkwiKQ0KDQppbmNpZGVudHNGTA0KDQp3ZWFwb25zICU+JQ0KICBmaWx0ZXIoaW5jaWRlbnRfaWQgPT0gMzAwKQ0KDQp2aWN0aW1zICU+JQ0KICBmaWx0ZXIoaW5jaWRlbnRfaWQgPT0gMzAwKQ0KDQpvZmZlbmRlcnMgJT4lDQogIGZpbHRlcihpbmNpZGVudF9pZCA9PSAzMDApDQpgYGANCg0KDQpIZXJlIEkgd2FzIGNoZWNraW5nIGlmIHRoZXJlIGFyZSBhbnkgb3RoZXIgZGlzY3JpbWluYXRvcnkgYXR0YWNrcyBhZ2FpbnN0IGdheSBwZW9wbGUgc2VhcmNoaW5nIHRocm91Z2ggdGhlIG5hcnJhdGl2ZXMgZm9yIHRoZSB0ZXJtICJnYXkiLCBzdXJwcmlzaW5nbHksIG91dCBvZiBhbGwgdGhlIG9uZXMgaW4gdGhlIFVTIG9ubHkgb25lIHNob3dlZCB1cCwgd2hpY2ggaXMgdGhlIHNhbWUgb25lIGZyb20gYWJvdmUgdGhhdCBvY2N1cnJlZCBpbiBPcmxhbmRvLg0KDQpgYGB7cn0NCiMgV2l0aCBncmVwbCBJIGNhbiBzZWFyY2ggZm9yIHNwZWNpZmljIHdvcmRzIGluIHRoZSBuYXJyYXRpdmUgb3IgYW55IG90aGVyIG9mIHRoZSBzdHJpbmdzIG9mIHRleHRzIHRoYXQgZ2l2ZSBuYXJyYXRpdmVzLg0KaW5jaWRlbnRzICU+JQ0KICBmaWx0ZXIoZ3JlcGwoImdheSIsIG5hcnJhdGl2ZSkpDQoNCmBgYA0KDQpgYGB7cn0NCg0KYGBgDQoNCkkgd291bGQgY29uc2lkZXIgdGhpcyBkYXRhIHNldCB0byBiZSAqcmVsYXRpdmVseSogY2xlYW4uIEJ1dCwgdGhlcmUgYXJlIHNvbWUgdGhpbmdzIHRoYXQgY291bGQgYmUgYWx0ZXJlZCBmb3IgdGhlIHNha2Ugb2Ygd29ya2luZyB3aXRoIHRoZW0uIEZvciBleGFtcGxlLCB0aGVyZSBhcmUgYSBsb3Qgb2YgTlVMTCBjZWxscyBidXQgZHVlIHRvIHRoZSB0eXBlIG9mIGRhdGEgSSBkbyBub3QgcGxhbiB0byByZW1vdmUgdGhlc2Ugcm93cywgSSB3aWxsIGhvd2V2ZXIgcHJvYmFibHkgY2hhbmdlIHRoZW0gaW50byAiTi9BIiBvciBzb21ldGhpbmcgb2YgdGhlIHNvcnQgYXMgdGhleSBhcmUgaW1wb3J0YW50LiBUaGVyZSBhcmUgc29tZSBmZWF0dXJlcywgZm9yIGV4YW1wbGUgLSBzaXR1YXRpb25fdHlwZSwgd2hpY2ggaGFzICJVbmRldGVybWluZXMiIGFuZCBqdXN0IGZsYXQgb3V0IE51bGwuIFJlYWRpbmcgdGhyb3VnaCB0aGUgbmFycmF0aXZlcywgbXkgcGVyc29uYWwgYmVsaWVmIGlzIHRoYXQgdGhlc2UgYXJlIG1vc3QgbGlrZWx5IGluY2lkZW50cyBpbiB3aGljaCB0aGVyZSB3YXMgc2ltcGx5IG5vIG9mZmljaWFsIHN0YXRlbWVudCBvZiB3aGF0IHR5cGUgb2Ygc2l0dWF0aW9uIHRoZSBpbmNpZGVudCB3YXMuIFRoaXMgd291bGQgYmUgYSBzaXR1YXRpb24gaW4gd2hpY2ggSSB3b3VsZCBhbHRlciB0aGUgZGF0YSB0byBvZmZpY2lhbGx5IHN0YXRlIHNvbWV0aGluZyBsaWtlICJOb3QgU3RhdGVkIiANCg0KDQoNCkRpc2N1c3Npb246DQoNCk15IGdlbmVyYWwgaW1wcmVzc2lvbiBvbiB0aGlzIGRhdGEgc2V0IGlzIHRoYXQgd2hpbGUgaXQgaXMgdmVyeSBpbnRlcmVzdGVkLCBhdCBsZWFzdCBmb3IgbWUsIGl0IGRvZXMgcG9zZSB0aGUgY2hhbGxlbmdlIG9mIGF0dGVtcHRpbmcgdG8gZG8gYW55IHNvcnQgb2YgcHJlZGljdGlvbnMuIEEgbG90IG9mIHRoaXMgZGF0YSBpcyBzaW1wbHkgZmFjdCBidXQgYmVjYXVzZSBvZiB0aGUgbmF0dXJlIG9mIHRoZSBkYXRhIEkgY2Fubm90IHNlZW0gdG8gZG8gYW55IHNvcnQgb2YgdGltZSBzZXJpZXMgZGF0YSBhcyB0aGUgZGF0ZXMgY291bGQgYmUgY29uc2lkZXJlZCBhcyBjb250aW51b3VzIGJ1dCB0aGUgc2l0dWF0aW9ucyBhcmUgc2VwYXJhdGUgYW5kIG5vdCBsaW5rZWQgdG9nZXRoZXIuDQoNClJlZ2FyZGluZyB0aGUgdHlwZSBvZiBtb2RlbHMsIEknbSB0aGlua2luZyBvZiBtYXliZSB1c2luZyBhIHR5cGUgb2YgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdG8gc2VlIGlmIG1heWJlIHRoZXJlIGlzIGFueSBjb3JyZWxhdGlvbiB3aXRoIGNlcnRhaW4gZmVhdHVyZXMgdG8gZGV0ZXJtaW5lL3ByZWRldGVybWluZSB0aGUgcG90ZW50aWFsIGxldGhhbGl0eSBvZiBhIHNpdHVhdGlvbiBiYXNlZCBvbiB0aGUgaW5mb3JtYXRpb24gaXQgaGFzIGdpdmVuIHVzLiBUaGlzIHdvdWxkIGJlIHRyaWNreSBhcyB0aGluZ3MgbGlrZSByYWNlIGFuZCBhZ2UgYXJlIHRvbyBicm9hZCBhbmQgb3RoZXIgZmVhdHVyZXMgbGlrZSBzaXR1YXRpb25fdHlwZSBhcmUgZ2VuZXJhbGx5IGhhcmQgdG8gZ2FndWUgYmVmb3JlIHRoZSBzaXR1YXRpb24gaGFzIGFscmVhZHkgZXNjYWxhdGVkLiBUaGF0IHNhaWQgSSdsbCBhdHRlbXB0IHRvIGF0IGxlYXN0IHNlZSB3aGF0IEkgd291bGQgYmUgYWJsZSB0byBwcmVkaWN0IGxpa2UgbWF5YmUgd2hhdCB0aGUgb2RkcyBhcmUgb2Ygc3Vydml2YWwgd2l0aCBzcGVjaWZpYyBib2R5IHR5cGVzIChhZ2UsIHNleCwgcmFjZSwgZXRjLikgaGF2ZSBpbiB0aGVpciByZXNwZWN0aXZlIHNpdHVhdGlvbnMuIFRoaXMgY291bGQgYmUgZG9uZSB3aXRoIHRoZSB1c2FnZSBvZiBsaW5lYXIgcmVncmVzc2lvbi4gDQoNCmBgYHtyfQ0KZnVsbE1lcmdlZCA9IDANCmZ1bGxNZXJnZWQgPC0gZnVsbF9qb2luKGluY2lkZW50cywgb2ZmZW5kZXJzLCBieSA9ICJpbmNpZGVudF9pZCIpDQpmdWxsTWVyZ2VkIDwtIGZ1bGxfam9pbihmdWxsTWVyZ2VkLCB2aWN0aW1zLCBieSA9ICJpbmNpZGVudF9pZCIpDQpmdWxsTWVyZ2VkIDwtIGZ1bGxfam9pbihmdWxsTWVyZ2VkLCB3ZWFwb25zLCBieSA9ICJpbmNpZGVudF9pZCIpDQoNCiMgeCA9IE9mZmVuZGVyLCB5ID0gVmljdGltDQpmdWxsTWVyZ2VkQ2xlYW4gPC0gc3Vic2V0KGZ1bGxNZXJnZWQsIHNlbGVjdCA9IC1jKGluY2lkZW50X2lkLCBsb2NhdGlvbl90eXBlLCBsb25naXR1ZGUsIGxhdGl0dWRlLCBjZW5zdXNfdHJhY3RfZ2VvaWQsIG5hcnJhdGl2ZSwgZmlyc3RuYW1lLCBsYXN0bmFtZSwgbWlkZGxlbmFtZSwgc3VmZml4LCBkZWF0aGNhdXNlLCBvdXRjb21lLCBjcmltaW5hbF9qdXN0aWNlX3Byb2Nlc3MsIHNlbnRlbmNlX3R5cGUsIHNlbnRlbmNlX2RldGFpbHMsIHZpY3RpbV9pZCwgd2VhcG9uX2lkLCBvZmZlbmRlcl9pZCwgdHlwZSkpDQpgYGANCg0KYGBge3J9DQpmdWxsTWVyZ2VkQ2xlYW4kaGFkX3N1cnZpdm9yIDwtIGlmZWxzZShmdWxsTWVyZ2VkQ2xlYW4kbnVtX3ZpY3RpbXNfaW5qdXJlZCA+IDAsICJZZXMiLCAiTm8iKQ0KZnVsbE1lcmdlZENsZWFuJGhhZF9zdXJ2aXZvciA8LSBhcy5mYWN0b3IoZnVsbE1lcmdlZENsZWFuJGhhZF9zdXJ2aXZvcikNCmBgYA0KDQpgYGB7cn0NCnVuaXF1ZShmdWxsTWVyZ2VkQ2xlYW4pDQoNCmZ1bGxNZXJnZWRDbGVhbiRkYXRlIDwtIHltZChmdWxsTWVyZ2VkQ2xlYW4kZGF0ZSkNCmZ1bGxNZXJnZWRDbGVhbiR5ZWFyIDwtIHllYXIoZnVsbE1lcmdlZENsZWFuJGRhdGUpDQpmdWxsTWVyZ2VkQ2xlYW4kbW9udGggPC0gbW9udGgoZnVsbE1lcmdlZENsZWFuJGRhdGUpDQpmdWxsTWVyZ2VkQ2xlYW4kZGF5IDwtIGRheXMoZnVsbE1lcmdlZENsZWFuJGRhdGUpDQpmdWxsTWVyZ2VkQ2xlYW4kd2Vla2RheSA8LSB3ZWVrZGF5cyhmdWxsTWVyZ2VkQ2xlYW4kZGF0ZSkNCg0KZmFjdG9yQ29sIDwtIGMoIndlZWtkYXkiLCAiY2l0eSIsICJzdGF0ZSIsICJmaXJzdGNvZCIsICJzZWNvbmRjb2QiLCAiaWZfYXNzYXVsdF9yaWZsZV91c2VkIiwgInNpdHVhdGlvbl90eXBlIiwgImxvY2F0aW9uIiwgImNvdW50eSIsICJyYWNlLngiLCAic2V4LngiLCAicmFjZS55IiwgInNleC55IiwgInZvcmVsYXRpb25zaGlwIiwgIndlYXBvbl90eXBlIiwgIndlYXBvbl90eXBlIiwgImd1bl9jbGFzcyIsICJndW5fdHlwZSIpDQoNCmZ1bGxNZXJnZWRDbGVhbiA8LSBmdWxsTWVyZ2VkQ2xlYW4gfD4NCiAgbXV0YXRlKGFjcm9zcyhhbGxfb2YoZmFjdG9yQ29sKSwgYXMuZmFjdG9yKSkNCg0KZnVsbE1lcmdlZENsZWFuJHN1aWNpZGUgPC0gYXMubnVtZXJpYyhmdWxsTWVyZ2VkQ2xlYW4kc3VpY2lkZSkNCg0KdGVzdE1lcmdlIDwtIHN1YnNldChmdWxsTWVyZ2VkQ2xlYW4sIHNlbGVjdCA9IGMod2Vla2RheSwgc3RhdGUsIG51bV9vZmZlbmRlcnMsIG51bV92aWN0aW1zX2tpbGxlZCwgbnVtX3ZpY3RpbXNfaW5qdXJlZCwgZmlyc3Rjb2QsIHNlY29uZGNvZCwgaWZfYXNzYXVsdF9yaWZsZV91c2VkLCBzaXR1YXRpb25fdHlwZSwgbG9jYXRpb24sIGFnZS54LCByYWNlLngsIHNleC54LCBhZ2UueSwgcmFjZS55LCBzZXgueSwgdm9yZWxhdGlvbnNoaXAsIHdlYXBvbl90eXBlLCBndW5fY2xhc3MsIGd1bl90eXBlLCBoYWRfc3Vydml2b3IpKQ0KDQp0ZXN0TWVyZ2UNCmBgYA0KDQoNCmBgYHtyfQ0KbGRhVGVzdE1vZGVsIDwtIGxkYShoYWRfc3Vydml2b3IgfiAuLCBkYXRhID0gdGVzdE1lcmdlKQ0Kc3VtbWFyeShsZGFUZXN0TW9kZWwpDQoNCmNvcih0ZXN0TWVyZ2UpDQpgYGANCg0KYGBge3J9DQppbmNpZGVudHMNCm9mZmVuZGVycw0KdmljdGltcw0Kd2VhcG9ucw0KYGBgDQoNCkZFQVRVUkUgRU5HSU5FRVIsIEdPSU5HIFRPIE1FUkdFIEVWRVJZVEhJTkcNCkZlYXR1cmVzIHdhbnRlZA0KaW5jaWRlbnRfaWQsIGRhdGUsIHN0YXRlLCBudW1fb2ZmZW5kZXJzLCBudW1fdmljdGltc19raWxsZWQsIG51bV92aWN0aW1zX2luanVyZWQsIGZpcnN0Y29kLCBpZl9hc3NhdWx0X3JpZmxlX3VzZWQsIHNpdHVhdGlvbl90eXBlLCBsb2NhdGlvbiwgb2ZmZW5kZXJfYWdlLCBvZmZlbmRlcl9yYWNlLCBvZmZlbmRlcl9zZXgsIHZpY3RpbV9hZ2UsIHZpY3RpbV9zZXgsIHZvcmVsYXRpb25zaGlwLCB3ZWFwb25fdHlwZQ0KDQpBREQNCm51bV92aWN0aW1zX3RvdGFsDQoNClR1cm4gQ2F0ZWdvcmljYWwgaW50byBkdW1teSB2YXJpYWJsZXMNCg0KYGBge3J9DQojIEZpeGluZyB0aGUgZGF0ZSBhbmQgYWRkaW5nIGV4dHJhIGRhdGUgY29sdW1ucy4NCmluY2lkZW50c1VwZGF0ZWQgPC0gaW5jaWRlbnRzICU+JQ0KICBtdXRhdGUoDQogICAgZGF0ZSA9IGFzLkRhdGUoZGF0ZSksDQogICAgeWVhciA9IHllYXIoZGF0ZSksDQogICAgbW9udGggPSBtb250aChkYXRlKSwNCiAgICBkYXkgPSBkYXkoZGF0ZSksDQogICAgd2Vla2RheSA9IHdlZWtkYXlzKGRhdGUpDQogICkNCg0KaW5jaWRlbnRzVXBkYXRlZCA8LSBuYS5vbWl0KGluY2lkZW50c1VwZGF0ZWQpDQoNCmluY2lkZW50c1VwZGF0ZWQkaGFkX3N1cnZpdm9yIDwtIGlmZWxzZShpbmNpZGVudHNVcGRhdGVkJG51bV92aWN0aW1zX2luanVyZWQgPiAwLCAiWWVzIiwgIk5vIikNCmluY2lkZW50c1VwZGF0ZWQkaGFkX3N1cnZpdm9yIDwtIGFzLmZhY3RvcihpbmNpZGVudHNVcGRhdGVkJGhhZF9zdXJ2aXZvcikNCg0KIyBbTmFtZV0gU3VtbWFyaWVzLCB0aGVzZSB3aWxsIGdldCBhbGwgdGhlIGNvbHVtbnMgdGhhdCB3aWxsIGJlIHVzZWQgZm9yIHRoZSBtb2RlbHMuDQppbmNpZGVudHNTdW0gPC0gaW5jaWRlbnRzVXBkYXRlZCAlPiUNCiAgc3Vic2V0KHNlbGVjdCA9IGMoaW5jaWRlbnRfaWQsIGRhdGUsIHllYXIsIG1vbnRoLCBkYXksIHdlZWtkYXksIHN0YXRlLCBudW1fb2ZmZW5kZXJzLCBudW1fdmljdGltc19raWxsZWQsIG51bV92aWN0aW1zX2luanVyZWQsIGZpcnN0Y29kLCBzaXR1YXRpb25fdHlwZSwgbG9jYXRpb24sIGhhZF9zdXJ2aXZvcikpDQoNCm9mZmVuZGVyU3VtIDwtIG9mZmVuZGVycyAlPiUNCiAgc3Vic2V0KHNlbGVjdCA9IGMoaW5jaWRlbnRfaWQsIG9mZmVuZGVyX2lkLCBhZ2UsIHJhY2UsIHNleCkpICU+JQ0KICByZW5hbWUob2ZmZW5kZXJBZ2UgPSBhZ2UpICU+JQ0KICByZW5hbWUob2ZmZW5kZXJSYWNlID0gcmFjZSkgJT4lDQogIHJlbmFtZShvZmZlbmRlclNleCA9IHNleCkNCg0KdmljdGltU3VtIDwtIHZpY3RpbXMgJT4lDQogIHN1YnNldChzZWxlY3QgPSBjKGluY2lkZW50X2lkLCB2aWN0aW1faWQsIGFnZSwgcmFjZSwgc2V4LCB2b3JlbGF0aW9uc2hpcCkpICU+JQ0KICByZW5hbWUodmljdGltQWdlID0gYWdlKSAlPiUNCiAgcmVuYW1lKHZpY3RpbVJhY2UgPSByYWNlKSAlPiUNCiAgcmVuYW1lKHZpY3RpbVNleCA9IHNleCkNCg0Kd2VhcG9uU3VtIDwtIHdlYXBvbnMgJT4lDQogIHN1YnNldChzZWxlY3QgPSBjKGluY2lkZW50X2lkLCB3ZWFwb25faWQsIHdlYXBvbl90eXBlKSkNCg0KIyBNZXJnaW5nIHRoZSBkYXRhDQp0b3RhbERhdGEgPC0gaW5jaWRlbnRzU3VtICU+JQ0KICBsZWZ0X2pvaW4ob2ZmZW5kZXJTdW0sIGJ5ID0gImluY2lkZW50X2lkIikgJT4lDQogIGxlZnRfam9pbih2aWN0aW1TdW0sIGJ5ID0gImluY2lkZW50X2lkIikgJT4lDQogIGxlZnRfam9pbih3ZWFwb25TdW0sIGJ5ID0gImluY2lkZW50X2lkIikNCg0KdG90YWxEYXRhIDwtIG5hLm9taXQodG90YWxEYXRhKQ0KdG90YWxEYXRhDQpgYGANCg0KYGBge3J9DQojIExpc3Qgb2YgY29sdW1uIG5hbWVzIEkgbmVlZCB0byBmYWN0b3JpemUNCmNoYXJFbmNvZGUgPC0gYygid2Vla2RheSIsICJzdGF0ZSIsICJmaXJzdGNvZCIsICJzaXR1YXRpb25fdHlwZSIsICJsb2NhdGlvbiIsICJvZmZlbmRlclJhY2UiLCAib2ZmZW5kZXJTZXgiLCAidmljdGltUmFjZSIsICJ2aWN0aW1TZXgiLCAidm9yZWxhdGlvbnNoaXAiLCAid2VhcG9uX3R5cGUiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBUdXJucyBhbnkgYmxhbmsgY2VsbHMgdG8gIlVua25vd24iDQp0b3RhbERhdGFbdG90YWxEYXRhID09ICIiXSA8LSAiVW5rbm93biINCg0KI2FueShpcy5uYSh0b3RhbERhdGEpKQ0KDQojIFRoaXMgd2lsbCBmYWN0b3JpemUgYWxsIG9mIHRoZSBkYXRhIGluc3RlYWQgb2YgaGF2aW5nIHRvIG1hbnVhbGx5IGRvIGl0DQp0b3RhbERhdGFGYWN0b3JlZCA8LSB0b3RhbERhdGEgJT4lDQogIG11dGF0ZShhY3Jvc3MoYWxsX29mKGNoYXJFbmNvZGUpLCB+IGFzLmludGVnZXIoZmFjdG9yKC54KSkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTdW1zIHRoZSBtYXggdmFsdWUgaW4gYWxsIG9mIHRoZSBjb2x1bW5zIHdoaWNoIHNob3VsZCwgaW4gdGhlb3J5LCBnaXZlIHRoZSB0b3RhbCBhbW91bnQgb2YgZGlzdGluY3QgY29sdW1ucyBJIHNob3VsZCBoYXZlLg0Kc3VtKHNhcHBseSh0b3RhbERhdGFGYWN0b3JlZFtjaGFyRW5jb2RlXSwgbWF4KSkNCg0KIyBHZXQgYWxsIG9mIHRoZSBuYW1lcyBhbmQgd2hpY2ggY29sdW1uIHRoZXkgd2VyZSBmcm9tDQpmYWN0b3JMb29rdXAgPC0gdG90YWxEYXRhICU+JQ0KICBkcGx5cjo6c2VsZWN0KGNoYXJFbmNvZGUpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIk9yaWdpbiIsDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiTmFtZSIpIA0KDQojIEdhdGhlcnMgYWxsIG9mIHRoZSBmYWN0b3JlZCBkYXRhDQpmYWN0b3JMb29rdXAyIDwtIHRvdGFsRGF0YUZhY3RvcmVkICU+JQ0KICBkcGx5cjo6c2VsZWN0KGNoYXJFbmNvZGUpICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gTlVMTCwNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJFbmNvZGVkIFZhbHVlIikNCg0KIyBBc3N1bWluZyBub3RoaW5nIGhhcyBjaGFuZ2VkIGFuZCBoYXZpbmcgY2hlY2tlZCB0aGUgbnVtYmVyIGl0IG1hdGNoZXMgdXAuIEkgYXNzdW1lIHRoaXMgaXMgY29ycmVjdCBhbmQgbm93IGZhY3Rvckxvb2t1cCBpcyBhIGdvb2QgZGljdGlvbmFyeSB0byBnbyBiYWNrIHRvLg0KZmFjdG9yTG9va3VwIDwtIGNiaW5kKGZhY3Rvckxvb2t1cCwgZmFjdG9yTG9va3VwMikNCmZhY3Rvckxvb2t1cCA8LSBkaXN0aW5jdChmYWN0b3JMb29rdXApDQpmYWN0b3JMb29rdXANCmBgYA0KDQpgYGB7cn0NCnNwbGl0RGF0YSA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGJpbmFyeUZhY3RvcmVkJGhhZF9zdXJ2aXZvciwgcCA9IDAuOCwgbGlzdCA9IEZBTFNFKQ0KdHJhaW5EYXRhIDwtIGJpbmFyeUZhY3RvcmVkW3NwbGl0RGF0YSwgXQ0KdGVzdERhdGEgPC0gYmluYXJ5RmFjdG9yZWRbLXNwbGl0RGF0YSwgXQ0KYGBgDQoNCg0KYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4fQ0KIyBUaGlzIGlzIHNvIHRoYXQgSSBjYW4gdXNlIGl0IGluIHRoZSBjb3JyIG1hdHJpeCBidXQgYWxzbyBtYWtlIG15IGxpZmUgZWFzaWVyDQp0b3RhbERhdGFGYWN0b3JlZEZvclJlYWwgPC0gc3Vic2V0KHRvdGFsRGF0YUZhY3RvcmVkLCBzZWxlY3QgPSAtYyhpbmNpZGVudF9pZCwgZGF0ZSwgeWVhciwgbW9udGgsIG9mZmVuZGVyX2lkLCB2aWN0aW1faWQpKQ0KdG90YWxEYXRhRmFjdG9yZWRGb3JSZWFsJGhhZF9zdXJ2aXZvciA8LSBhcy5udW1lcmljKHRvdGFsRGF0YUZhY3RvcmVkRm9yUmVhbCRoYWRfc3Vydml2b3IpDQoNCiMgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwNCmxpbk1vZGVsIDwtIGxtKGhhZF9zdXJ2aXZvciB+IC4sIGRhdGEgPSB0b3RhbERhdGFGYWN0b3JlZEZvclJlYWwpDQpzdW1tYXJ5KGxvZ01vZGVsKQ0KDQpjb3JNIDwtIGNvcih0b3RhbERhdGFGYWN0b3JlZEZvclJlYWwpDQpjb3JycGxvdChjb3JNLCBtZXRob2QgPSAiY29sb3IiKQ0KaGVhdG1hcChjb3JNLCBzeW1tID0gVFJVRSwgbWFpbiA9ICJDb3JyZWxhdGlvbiBIZWF0bWFwIikNCmBgYA0KDQpgYGB7cn0NCiMgSy1Gb2xkIFRyYWluIENvbnRyb2wNCnNldC5zZWVkKDc4OSkNCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgbnVtYmVyID0gMTAsIHJlcGVhdHMgPSAzLCBjbGFzc1Byb2JzID0gVFJVRSwgc3VtbWFyeUZ1bmN0aW9uID0gdHdvQ2xhc3NTdW1tYXJ5LCB2ZXJib3NlSXRlciA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0KYmluYXJ5RmFjdG9yZWQgPC0gc3Vic2V0KHRvdGFsRGF0YUZhY3RvcmVkLCBzZWxlY3QgPSAtYyhpbmNpZGVudF9pZCwgZGF0ZSwgeWVhciwgbW9udGgsIG9mZmVuZGVyX2lkLCB2aWN0aW1faWQpKQ0KYmluYXJ5RmFjdG9yZWQkaGFkX3N1cnZpdm9yIDwtIGZhY3Rvcih0b3RhbERhdGFGYWN0b3JlZCRoYWRfc3Vydml2b3IsIGxldmVscyA9IGMoIlllcyIsICJObyIpKQ0KYmluYXJ5RmFjdG9yZWQNCg0KdGFibGUoYmluYXJ5RmFjdG9yZWQkaGFkX3N1cnZpdm9yKQ0KDQpyZk1vZGVsIDwtIHRyYWluKGhhZF9zdXJ2aXZvciB+IC4gLWhhZF9zdXJ2aXZvciAtbnVtX3ZpY3RpbXNfa2lsbGVkIC1udW1fdmljdGltc19pbmp1cmVkLCBkYXRhID0gYmluYXJ5RmFjdG9yZWQsIG1ldGhvZCA9ICJyZiIsIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsIG50cmVlID0gNTAwKQ0KcHJpbnQocmZNb2RlbCkNCg0KYGBgDQoNCg0KYGBge3J9DQojIExvZ2lzdGljIFJlZ3Jlc3Npb246IENyZWF0aW5nIHRoZSBpbml0aWFsIG1vZGVsDQpsb2dNb2RlbCA8LSBnbG0oaGFkX3N1cnZpdm9yfiAuIC1udW1fdmljdGltc19raWxsZWQgLW51bV92aWN0aW1zX2luanVyZWQgLWhhZF9zdXJ2aXZvciwgZGF0YSA9IHRyYWluRGF0YSwgZmFtaWx5ID0gImJpbm9taWFsIikNCg0KIyBBcHBseWluZyBzdGVwd2lzZQ0Kc3RlcExvZ01vZGVsIDwtIHN0ZXAobG9nTW9kZWwsIGRpcmVjdGlvbiA9ICJib3RoIikNCg0KIyBUcmFpbmluZyB0aGUgc3RlcHdpc2Ugb24ga0NWDQpzdGVwTG9nTW9kZWxDViA8LSB0cmFpbihoYWRfc3Vydml2b3IgfiAuIC1udW1fdmljdGltc19raWxsZWQgLW51bV92aWN0aW1zX2luanVyZWQgLWhhZF9zdXJ2aXZvciwgZGF0YSA9IHRyYWluRGF0YSwgbWV0aG9kID0gImdsbSIsIGZhbWlseSA9ICJiaW5vbWlhbCIsIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsIG1ldHJpYyA9ICJST0MiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMb2dpc3RpYyBSZWdyZXNzaW9uIHdpdGggU3RlcCBXaXNlIGFuZCBrQ1YNCnN0ZXBMb2dQcmVkIDwtIHByZWRpY3Qoc3RlcExvZ01vZGVsQ1YsIG5ld2RhdGEgPSB0ZXN0RGF0YSwgdHlwZSA9ICJwcm9iIikNCnJvY09iIDwtIHJvYyh0ZXN0RGF0YSRoYWRfc3Vydml2b3IsIHN0ZXBMb2dQcmVkJFllcykNCnBsb3Qocm9jT2IsIHByaW50LmF1YyA9IFRSVUUsDQogICAgICAgICBjb2wgPSAiYmx1ZSIsDQogICAgICAgICBsd2QgPSAzLA0KICAgICAgICAgbGVnYWN5LmF4ZXMgPSBUUlVFLA0KICAgICAgICAgbWFpbiA9ICJST0MgQ3VydmUgZm9yIFN1cnZpdmFsIFByZWRpY3Rpb24iKQ0KYWJsaW5lKGEgPSAwLCBiID0gMSwgbHR5ID0gMiwgY29sID0gInJlZCIpDQphdWMocm9jT2IpDQpgYGANCg0KDQoNCg==